home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / dte5_1.zip / UTILS.C < prev    next >
C/C++ Source or Header  |  1991-02-06  |  56KB  |  1,884 lines

  1. /*
  2.  * Written by Douglas Thomson (1989/1990)
  3.  *
  4.  * This source code is released into the public domain.
  5.  */
  6.  
  7. /*
  8.  * Name:    dte - Doug's Text Editor program - miscellaneous utilities
  9.  * Purpose: This file contains miscellaneous functions that were required
  10.  *           in more than one of the other files, or were thought to be
  11.  *           likely to be used elsewhere in the future.
  12.  * File:    utils.c
  13.  * Author:  Douglas Thomson
  14.  * System:  this file is intended to be system-independent
  15.  * Date:    October 1, 1989
  16.  */
  17.  
  18. #ifdef HPXL
  19. #include "commonh"
  20. #include "utilsh"
  21. #else
  22. #include "common.h"
  23. #include "utils.h"
  24. #endif
  25. #include <time.h>
  26. #ifdef __TURBOC__
  27. #include <dir.h>        /* for making temporary file names etc */
  28. #endif
  29.  
  30. /*
  31.  * prototypes for all functions in this file
  32.  */
  33. int myisalnum ARGS((char c));
  34. int linelen ARGS((text_ptr s));
  35. int prelinelen ARGS((text_ptr s));
  36. text_ptr find_next ARGS((text_ptr s));
  37. text_ptr find_prev ARGS((text_ptr current));
  38. void copy_line ARGS((windows *window));
  39. void un_copy_line ARGS((windows *window));
  40. int expand ARGS((text_ptr dest, text_ptr end));
  41. int load_file ARGS((char *name, int fixup));
  42. void set_prompt ARGS((char *prompt, int lines));
  43. int get_name ARGS((char *prompt, int lines, char *name));
  44. void fix_marks ARGS((windows *window, text_ptr pos, long len));
  45. int get_ynaq ARGS((windows *window));
  46. int get_yn ARGS((windows *window));
  47. int get_oa ARGS((windows *window));
  48. char get_attr ARGS((windows *window, text_ptr text));
  49. int update_line ARGS((windows *window, text_ptr orig, int line,
  50.         text_ptr cursor));
  51. int display_window ARGS((windows *window, int last, text_ptr cursor, int wn));
  52. int display ARGS((do_func doit, int reserved));
  53. void setup_window ARGS((windows *window));
  54. int first_non_blank ARGS((char *s));
  55. void page_up ARGS((windows *window));
  56. void page_down ARGS((windows *window));
  57. void scroll_down ARGS((windows *window));
  58. void scroll_up ARGS((windows *window));
  59. void save_file ARGS((windows *window, int kind));
  60. void save_as_file ARGS((windows *window));
  61.  
  62. /*
  63.  * Name:    myisalnum
  64.  * Purpose: To determine whether or not a character is part of a "word",
  65.  *           which in languages like Pascal means a letter, digit or
  66.  *           underscore.
  67.  * Date:    October 1, 1989
  68.  * Passed:  c: the character to be tested
  69.  * Returns: TRUE if c is an alphanumeric or '_' character, FALSE otherwise
  70.  */
  71. int myisalnum(c)
  72. char c;
  73. {
  74.     if ((c >= 'A' && c <= 'Z') ||
  75.             (c >= 'a' && c <= 'z') ||
  76.             (c >= '0' && c <= '9') ||
  77.             (c == '_')) {
  78.         return TRUE;
  79.     }
  80.     return FALSE;
  81. }
  82.  
  83. /*
  84.  * Name:    linelen
  85.  * Purpose: To determine the length of a line, up to either a \n or a
  86.  *           \0, whichever comes first.
  87.  * Date:    October 1, 1989
  88.  * Passed:  s: the line to be measured
  89.  * Returns: the length of the line
  90.  */
  91. int linelen(s)
  92. text_ptr s;
  93. {
  94.     int len = 0;
  95.  
  96.     while (*s && *s != '\n') {
  97.         ++len;
  98.         ++s;
  99.     }
  100.     return len;
  101. }
  102.  
  103. /*
  104.  * Name:    prelinelen
  105.  * Purpose: To determine the length of a line, from the current position
  106.  *           backwards to either a \n or a \0, whichever comes first.
  107.  * Date:    October 1, 1989
  108.  * Passed:  s: the line to be measured
  109.  * Returns: the length of the line up to the current position
  110.  * Notes:   It is assumed there will be a "terminating" \0 before the
  111.  *           start of the first line. This is the case with the main
  112.  *           text buffer, but elsewhere beware.
  113.  */
  114. int prelinelen(s)
  115. text_ptr s;
  116. {
  117.     int len = 0;
  118.  
  119.     while (*--s && *s != '\n') {
  120.         ++len;
  121.     }
  122.     return len;
  123. }
  124.  
  125. /*
  126.  * Name:    find_next
  127.  * Purpose: To find the first character in the next line after the starting
  128.  *           point.
  129.  * Date:    October 1, 1989
  130.  * Passed:  s: the starting point
  131.  * Returns: the first character in the next line
  132.  */
  133. text_ptr find_next(s)
  134. text_ptr s;
  135. {
  136.     while (*s && *s != '\n') {
  137.         ++s;
  138.     }
  139.     if (*s) {
  140.         return ++s;
  141.     }
  142.     return NULL;
  143. }
  144.  
  145. /*
  146.  * Name:    find_prev
  147.  * Purpose: To find the start of the line before the current line.
  148.  * Date:    October 1, 1989
  149.  * Passed:  current: the current line
  150.  * Returns: the start if the previous line
  151.  * Notes:   current must be at the start of the current line to begin with.
  152.  *          There must be a \0 preceding the first line.
  153.  */
  154. text_ptr find_prev(current)
  155. text_ptr current;
  156. {
  157.     if (*--current == '\0') {
  158.         return NULL;
  159.     }
  160.     for (;;) {
  161.         if (*--current == '\n' || *current == '\0') {
  162.             return ++current;
  163.         }
  164.     }
  165. }
  166.  
  167. /*
  168.  * Name:    copy_line
  169.  * Purpose: To copy the cursor line, if necessary, into the current line
  170.  *           buffer, so that changes can be made efficiently.
  171.  * Date:    October 1, 1989
  172.  * Passed:  window: access to the current line
  173.  * Notes:   As the cursor line is being copied, any markers that are set
  174.  *           within the line are also copied.
  175.  *          Trailing spaces left on the line (presumably from earlier
  176.  *           editing) are removed during the copy.
  177.  *          See un_copy_line, the reverse operation.
  178.  */
  179. void copy_line(window)
  180. windows *window;
  181. {
  182.     text_ptr p, q;     /* destination and source of copy */
  183.     int count;         /* number of characters copied */
  184.     int i;             /* for updating markers */
  185.     text_ptr end_line; /* end of line after removing trailing spaces */
  186.  
  187.     /*
  188.      * If the line has already been copied, then do not copy it again
  189.      */
  190.     if (g_status.copied) {
  191.         return;
  192.     }
  193.  
  194.     /*
  195.      * record that the current line buffer is active
  196.      */
  197.     g_status.copied = TRUE;
  198.  
  199.     /*
  200.      * clear any old buffer markers left from last time
  201.      */
  202.     for (i=0; i < NO_MARKS; i++) {
  203.         g_status.buff_marker[i] = NULL;
  204.     }
  205.  
  206.     /*
  207.      * find out where the line should end after removing trailing
  208.      *  spaces.
  209.      */
  210.     end_line = q = window->cursor;
  211.     while (*q && *q != '\n') {
  212.         if (*q++ != ' ') {
  213.             end_line = q;
  214.         }
  215.     }
  216.  
  217.     /*
  218.      * copy the cursor line to the line buffer, noting any markers
  219.      *  passed along the way
  220.      */
  221.     p = g_status.line_buff;
  222.     q = window->cursor;
  223.     for (count=0; ; ) {
  224.         for (i=0; i < NO_MARKS; i++) {
  225.             if (q == window->file_info->marker[i]) {
  226.                 g_status.buff_marker[i] = p;
  227.             }
  228.         }
  229.         if (*q == '\n') {
  230.             *p++ = *q++;
  231.             break;
  232.         }
  233.         if (*q == '\0') {
  234.             break;
  235.         }
  236.  
  237.         /*
  238.          * avoid copying trailing spaces
  239.          */
  240.         if (q < end_line) {
  241.             if (++count >= BUFF_SIZE) {
  242.                 error(WARNING, "line buffer overflow - line truncated!");
  243.                 break;
  244.             }
  245.             *p++ = *q;
  246.         }
  247.         ++q;
  248.     }
  249.     *p = '\0';
  250. }
  251.  
  252. /*
  253.  * Name:    un_copy_line
  254.  * Purpose: To copy the cursor line, if necessary, from the current line
  255.  *           buffer, shifting the main text to make the right amount of
  256.  *           room.
  257.  * Date:    October 1, 1989
  258.  * Passed:  window: access to the current line
  259.  * Notes:   As the cursor line is being copied, any markers that are set
  260.  *           within the line buffer are also copied.
  261.  *          For various reasons, trailing spaces are NOT removed when
  262.  *           returning the line buffer to the main text. Typically,
  263.  *           padding is added at the end of a line by deliberately
  264.  *           adding trailing spaces, and then uncopying the line.
  265.  *          See copy_line, the reverse operation.
  266.  */
  267. void un_copy_line(window)
  268. windows *window;
  269. {
  270.     text_ptr source; /* source for block move and for copying buffer line */
  271.     text_ptr dest;   /* destination for block move and copy */
  272.     long number;     /* length of block move */
  273.     int len;         /* length of current line buffer text */
  274.     int curs_len;    /* length of cursor line */
  275.     int i;           /* used for checking markers */
  276.  
  277.     /*
  278.      * do not uncopy unless the line buffer is active
  279.      */
  280.     if (!g_status.copied) {
  281.         return;
  282.     }
  283.  
  284.     /*
  285.      * record that the line buffer is not active
  286.      */
  287.     g_status.copied = FALSE;
  288.  
  289.     /*
  290.      * work out the lengths of the old cursor line (including the \n if any)
  291.      *  and the new current line buffer text.
  292.      */
  293.     curs_len = linelen(window->cursor);
  294.     if (window->cursor[curs_len] == '\n') {
  295.         ++curs_len;
  296.     }
  297.     len = strlen(g_status.line_buff);
  298.  
  299.     /*
  300.      * if the main text buffer has run out of space, then only part of the
  301.      *  current line can be moved back into the main buffer. Warn the user
  302.      *  that some of the current line has been lost
  303.      */
  304.     if (g_status.end_mem + len - curs_len >= g_status.max_mem) {
  305.         error(WARNING, "buffer full, part line truncated");
  306.         len = curs_len + (int) (g_status.max_mem - g_status.end_mem);
  307.         g_status.line_buff[len] = '\0';
  308.     }
  309.  
  310.     /*
  311.      * move text to either make room for the extra characters in the new
  312.      *  line, or else close up the gap.
  313.      */
  314.     source = window->cursor + curs_len;
  315.     dest = source + len - curs_len;
  316.     number = g_status.end_mem - source;
  317.     hw_move(dest, source, number);
  318.  
  319.     /*
  320.      * adjust any markers that were set after the original cursor line
  321.      */
  322.     fix_marks(window, window->cursor, (long) (len - curs_len));
  323.  
  324.     /*
  325.      * now copy the line buffer into the space just created, updating any
  326.      *  markers found in the line buffer
  327.      */
  328.     source = g_status.line_buff;
  329.     dest = window->cursor;
  330.     for (;;) {
  331.         for (i=0; i < NO_MARKS; i++) {
  332.             if (g_status.buff_marker[i] == source) {
  333.                 window->file_info->marker[i] = dest;
  334.             }
  335.         }
  336.         if (*source == '\0') {
  337.             break;
  338.         }
  339.         *dest++ = *source++;
  340.     }
  341. }
  342.  
  343. /*
  344.  * Name:    expand
  345.  * Purpose: To expand tabs in text from an input file.
  346.  * Date:    October 1, 1989
  347.  * Passed:  dest:   start of text to expand
  348.  *          end:    end of text to expand
  349.  * Returns: OK if text fitted in buffer and was reasonable text
  350.  *          ERROR if any problem
  351.  * Notes:   Tabs are expanded using the current tab interval.
  352.  *          Lines are checked to make sure they are not too long.
  353.  *          Characters are checked to ensure a NULL character does not
  354.  *           get into the buffer.
  355.  *          Originally, this function was called for all file reads.
  356.  *           However, it proved rather slow, and unnecessary for most
  357.  *           files. The only real danger is a NULL character, which the
  358.  *           editor will treat as the end of the buffer.
  359.  */
  360. int expand(dest, end)
  361. text_ptr dest;
  362. text_ptr end;
  363. {
  364.     int spaces = 0;  /* spaces still to be inserted to make tab */
  365.     int count = 0;   /* characters on the current line */
  366.     long number;     /* bytes in original text */
  367.     text_ptr source; /* current position in copied original text */
  368.     char c;          /* current character from source */
  369.     char lastc = 0;  /* character before c */
  370.     int noinc;       /* do not increment source? */
  371.  
  372.     /*
  373.      * first copy entire text to the very end of the buffer, so
  374.      *  that it can be copied back to the start while expanding tabs
  375.      */
  376.     number = end - dest;
  377.     source = dest + (g_status.max_mem - end);
  378.     hw_move(source, dest, number); /* note source and dest reversed here */
  379.     end = g_status.max_mem;
  380.  
  381.     /*
  382.      * keep on processing until end of text or some kind of error
  383.      */
  384.     while (source < end) {
  385.         c = *source;
  386.  
  387.         /*
  388.          * check main text buffer still has room
  389.          */
  390.         if (dest >= end) {
  391.             error(WARNING, "buffer full");
  392.             return ERROR;
  393.         }
  394.  
  395.         /*
  396.          * if the last character was a tab, then pretend the right number
  397.          *  of spaces occurred in the file
  398.          */
  399.         if ((noinc = spaces) != 0) {
  400.             --spaces;
  401.             c = ' ';
  402.         }
  403.  
  404.         /*
  405.          * process next character
  406.          */
  407.         if (++count >= BUFF_SIZE) {
  408.             /*
  409.              * current line too long to handle
  410.              */
  411.             error(WARNING, "line too long");
  412.             return ERROR;
  413.         }
  414.         else if (c == '\n') {
  415.             /*
  416.              * end of line - now remove trailing spaces
  417.              */
  418.             if (lastc == ' ') {
  419.                 while (*--dest == ' ') {
  420.                     ;
  421.                 }
  422.                 ++dest;
  423.             }
  424.             count = 0;
  425.         }
  426.         else if (c == '\t') {
  427.             /*
  428.              * work out how many spaces are required to expand the tab
  429.              */
  430.             spaces = g_status.tab_size - ((count-1) % g_status.tab_size) - 1;
  431.             c = ' ';
  432.         }
  433.         else if (c == 0) {
  434.             /*
  435.              * this would confuse things rather...
  436.              */
  437.             error(WARNING, "cannot handle NULL characters");
  438.             return ERROR;
  439.         }
  440.         if (!noinc) {
  441.             source++;
  442.         }
  443.         *dest++ = c;
  444.         lastc = c;
  445.     }
  446.  
  447.     /*
  448.      * record the end of the text just read
  449.      */
  450.     g_status.temp_end = dest;
  451.     return OK;
  452. }
  453.  
  454. /*
  455.  * Name:    load_file
  456.  * Purpose: To read in a given file to the end of the main text buffer.
  457.  * Date:    October 1, 1989
  458.  * Passed:  name:   path name of file to be read
  459.  *          fixup:  should tabs be expanded after reading?
  460.  * Returns: OK if file read successfully
  461.  *          ERROR if any problem (such as out of buffer space)
  462.  * Notes:   If the file does not exist, the user is informed of an error,
  463.  *           so check first if it is OK for the file to be new.
  464.  */
  465. int load_file(name, fixup)
  466. char *name;
  467. int fixup;
  468. {
  469.     /*
  470.      * make sure this gets set properly even if there is no file!
  471.      */
  472.     g_status.temp_end = g_status.end_mem;
  473.  
  474.     if (hw_load(name, g_status.end_mem, g_status.max_mem,
  475.             &g_status.temp_end) == ERROR) {
  476.         return ERROR;
  477.     }
  478.  
  479.     if (fixup) {
  480.         /*
  481.          * expand tabs and check for printable characters only
  482.          */
  483.         error(TEMP, "expanding tabs...");
  484.         return expand(g_status.end_mem, g_status.temp_end);
  485.     }
  486.     else {
  487.         return OK;
  488.     }
  489. }
  490.  
  491. /*
  492.  * Name:    set_prompt
  493.  * Purpose: To display a prompt, highlighted, at the bottom of the screen.
  494.  * Date:    October 1, 1989
  495.  * Passed:  prompt: prompt to be displayed
  496.  *          lines:  how many lines up from the bottom (usually just 1)
  497.  * Notes:   We use update_line to display the prompt, since it can deal
  498.  *           with clearing to end of line even on terminals without such
  499.  *           a command.
  500.  */
  501. void set_prompt(prompt, lines)
  502. char *prompt;
  503. int lines;
  504. {
  505.     text_ptr old_start;    /* for saving match location */
  506.     text_ptr old_end;      /*  "  */
  507.     windows empty_window;  /* window with no marked text */
  508.     file_infos empty_file; /* file with no marked text */
  509.  
  510.     /*
  511.      * save current matched text
  512.      */
  513.     old_start = g_status.match_start;
  514.     old_end = g_status.match_end;
  515.  
  516.     /*
  517.      * work out where the answer should go
  518.      */
  519.     g_status.prompt_col = strlen(prompt);
  520.     g_status.prompt_line = g_display.nlines - lines;
  521.  
  522.     /*
  523.      * cause the prompt to be highlighted
  524.      */
  525.     g_status.match_start = prompt;
  526.     g_status.match_end = prompt + g_status.prompt_col;
  527.  
  528.     /*
  529.      * set up a window which will not have anything except normal
  530.      *  attributes
  531.      */
  532.     empty_window.file_info = &empty_file;
  533.     empty_file.visible = FALSE;
  534.  
  535.     /*
  536.      * output the prompt
  537.      */
  538.     update_line(&empty_window, prompt, g_display.nlines-lines,
  539.             NULL);
  540.  
  541.     /*
  542.      * restore the old matched text
  543.      */
  544.     g_status.match_start = old_start;
  545.     g_status.match_end = old_end;
  546.  
  547.     /*
  548.      * ensure the cursor is in the right place
  549.      */
  550.     xygoto(g_status.prompt_col, g_status.prompt_line);
  551. }
  552.  
  553. /*
  554.  * Name:    get_name
  555.  * Purpose: To prompt the user, and read the string the user enters in
  556.  *           response.
  557.  * Date:    October 1, 1989
  558.  * Passed:  prompt: prompt to offer the user
  559.  *          lines:  no. of lines up from the bottom of the screen
  560.  *          name:   default answer
  561.  * Returns: name:   user's answer
  562.  *          OK if user entered something
  563.  *          ERROR if user aborted the command
  564.  * Notes:   Editing of the line is supported.
  565.  */
  566. int get_name(prompt, lines, name)
  567. char *prompt;
  568. int lines;
  569. char *name;
  570. {
  571.     int col;                /* cursor column for answer */
  572.     int line;               /* cursor line for answer */
  573.     int c;                  /* character user just typed */
  574.     char *cp;               /* cursor position in answer */
  575.     char *answer;           /* user's answer */
  576.     int first = TRUE;       /* first character typed */
  577.     int len;                /* length of answer */
  578.     int plen;               /* length of prompt */
  579.     char *p;                /* for copying text in answer */
  580.     char buffer[MAX_COLS+1];/* line on which name is being entered */
  581.     windows empty_window;  /* window with no marked text */
  582.     file_infos empty_file; /* file with no marked text */
  583.  
  584.     /*
  585.      * set up prompt and default
  586.      */
  587.     strcpy(buffer, prompt);
  588.     plen = strlen(prompt);
  589.     answer = buffer + plen;
  590.     strcpy(answer, name);
  591.  
  592.     /*
  593.      * set up a window which will not have anything except normal
  594.      *  attributes
  595.      */
  596.     empty_window.file_info = &empty_file;
  597.     empty_file.visible = FALSE;
  598.  
  599.     /*
  600.      * let user edit default into desired string
  601.      */
  602.     len = strlen(answer);
  603.     col = strlen(buffer);
  604.     line = g_display.nlines - lines;
  605.     cp = answer + len;
  606.     for (;;) {
  607.         /*
  608.          * cause the prompt to be highlighted
  609.          */
  610.         g_status.match_start = buffer;
  611.         g_status.match_end = buffer + len + plen;
  612.  
  613.         /*
  614.          * output the line
  615.          */
  616.         update_line(&empty_window, buffer, line, NULL);
  617.  
  618.         /*
  619.          * remove highlighting
  620.          */
  621.         g_status.match_start = g_status.match_end = NULL;
  622.  
  623.         /*
  624.          * place cursor in correct position
  625.          */
  626.         xygoto(col, line);
  627.  
  628.         /*
  629.          * process next keystroke
  630.          */
  631.         if ((c = c_input()) == '\r') {
  632.             /*
  633.              * finished
  634.              */
  635.             break;
  636.         }
  637.         if (c == '\b') {
  638.             /*
  639.              * delete to left of cursor
  640.              */
  641.             if (cp > answer) {
  642.                 for (p=cp-1; p < answer+len; p++) {
  643.                     *p = *(p+1);
  644.                 }
  645.                 --len;
  646.                 --col;
  647.                 --cp;
  648.                 xygoto(col, line);
  649.                 c_delete();
  650.             }
  651.         }
  652.         else if (c == CONTROL('G') || c == 127) {
  653.             /*
  654.              * delete char under cursor
  655.              */
  656.             if (*cp) {
  657.                 for (p=cp; p < answer+len; p++) {
  658.                     *p = *(p+1);
  659.                 }
  660.                 --len;
  661.                 c_delete();
  662.             }
  663.         }
  664.         else if (c == CONTROL('Y')) {
  665.             /*
  666.              * delete current line
  667.              */
  668.             col = plen;
  669.             cp = answer;
  670.             *cp = '\0';
  671.             len = 0;
  672.         }
  673.         else if (c == CONTROL('R')) {
  674.             /*
  675.              * restore original line
  676.              */
  677.             strcpy(answer, name);
  678.             len = strlen(answer);
  679.             col = plen + len;
  680.             cp = answer + len;
  681.         }
  682.         else if (c == CONTROL('S')) {
  683.             /*
  684.              * move cursor left
  685.              */
  686.             if (cp > answer) {
  687.                 col--;
  688.                 cp--;
  689.             }
  690.         }
  691.         else if (c == CONTROL('D')) {
  692.             /*
  693.              * move cursor right
  694.              */
  695.             if (*cp) {
  696.                 col++;
  697.                 cp++;
  698.             }
  699.         }
  700.         else if (c == CONTROL('E')) {
  701.             /*
  702.              * move cursor to start of line
  703.              */
  704.             col = plen;
  705.             cp = answer;
  706.         }
  707.         else if (c == CONTROL('X')) {
  708.             /*
  709.              * move cursor to end of line
  710.              */
  711.             col = plen + len;
  712.             cp = answer + len;
  713.         }
  714.         else if (hw_printable(c)) {
  715.             /*
  716.              * insert character at cursor
  717.              */
  718.             if (first) {
  719.                 /*
  720.                  * delete previous answer
  721.                  */
  722.                 col = plen;
  723.                 cp = answer;
  724.                 *cp = '\0';
  725.                 len = 0;
  726.             }
  727.  
  728.             /*
  729.              * insert new character
  730.              */
  731.             if (col < g_display.ncols-1) {
  732.                 for (p=answer+len; p >= cp; p--) {
  733.                     *(p+1) = *p;
  734.                 }
  735.                 *cp = c;
  736.                 c_insert();
  737.                 c_output(c);
  738.                 ++cp;
  739.                 ++len;
  740.                 ++col;
  741.             }
  742.         }
  743.         else if (c == 27 || c == CONTROL('U')) {
  744.             /*
  745.              * abort operation
  746.              */
  747.             return ERROR;
  748.         }
  749.         first = FALSE;
  750.     }
  751.  
  752.     /*
  753.      * finally, replace the default
  754.      */
  755.     strcpy(name, answer);
  756.     return OK;
  757. }
  758.  
  759. /*
  760.  * Name:    fix_marks
  761.  * Purpose: To make the necessary adjustments to the appropriate markers
  762.  *           after characters have been inserted or deleted.
  763.  * Date:    October 1, 1989
  764.  * Passed:  window: access to the current window, buffers and markers
  765.  *          pos:    position of insertion or deletion
  766.  *          len:    length of insertion or (if negative) deletion
  767.  */
  768. void fix_marks(window, pos, len)
  769. windows *window;
  770. text_ptr pos;
  771. long len;
  772. {
  773.     int i;              /* used to can through markers */
  774.     text_ptr other;     /* end of deleted area */
  775.     file_infos *file;   /* for scanning markers in other files */
  776.     windows *wp;        /* for checking cursor lines */
  777.  
  778.     /*
  779.      * If the cursor line was copied into the line buffer (perhaps
  780.      *  for moving the cursor by a word) and then copied back with
  781.      *  no change, then there is nothing to do here.
  782.      */
  783.     if (len == 0) {
  784.         return;
  785.     }
  786.  
  787.     if (pos >= g_status.start_mem && pos < g_status.end_mem) {
  788.         /*
  789.          * the insert/delete affected the total text
  790.          */
  791.         if (len >= 0) {
  792.             /*
  793.              * adjust file position markers of all files
  794.              */
  795.             for (file=g_status.file_list; file; file = file->next) {
  796.                 for (i=0; i < NO_MARKS; i++) {
  797.                     if (file->marker[i] > pos) {
  798.                         file->marker[i] += len;
  799.                     }
  800.                 }
  801.             }
  802.  
  803.             /*
  804.              * adjust cursor lines of other windows
  805.              */
  806.             for (wp=g_status.window_list; wp; wp = wp->next) {
  807.                 if (wp != window) {
  808.                     if (wp->cursor > pos) {
  809.                         wp->cursor += len;
  810.                     }
  811.                 }
  812.             }
  813.         }
  814.         else {
  815.             other = pos - len;
  816.             /*
  817.              * adjust file position markers of all files
  818.              */
  819.             for (file=g_status.file_list; file; file = file->next) {
  820.                 for (i=0; i < NO_MARKS; i++) {
  821.                     if (file->marker[i] >= other) {
  822.                         file->marker[i] += len;
  823.                     }
  824.                     else if (file->marker[i] > pos) {
  825.                         file->marker[i] = pos;
  826.                     }
  827.                 }
  828.             }
  829.  
  830.             /*
  831.              * adjust cursor lines of other windows
  832.              */
  833.             for (wp=g_status.window_list; wp; wp = wp->next) {
  834.                 if (wp != window) {
  835.                     if (wp->cursor >= other) {
  836.                         wp->cursor += len;
  837.                     }
  838.                     else if (wp->cursor > pos) {
  839.                         wp->cursor = pos;
  840.                     }
  841.                 }
  842.             }
  843.         }
  844.  
  845.         /*
  846.          * adjust file buffer beginning and ending positions for
  847.          *  all files
  848.          */
  849.         for (file=g_status.file_list; file; file = file->next) {
  850.             if (file->start_text > pos) {
  851.                 file->start_text += len;
  852.             }
  853.             if (file->end_text > pos) {
  854.                 file->end_text += len;
  855.             }
  856.         }
  857.  
  858.         /*
  859.          * adjust total memory buffer size
  860.          */
  861.         g_status.end_mem += len;
  862.     }
  863.     else {
  864.         /*
  865.          * the insert/delete only affected the current line
  866.          */
  867.         if (len >= 0) {
  868.             for (i=0; i < NO_MARKS; i++) {
  869.                 if (g_status.buff_marker[i] > pos) {
  870.                     g_status.buff_marker[i] += len;
  871.                 }
  872.             }
  873.         }
  874.         else {
  875.             other = pos - len;
  876.             for (i=0; i < NO_MARKS; i++) {
  877.                 if (g_status.buff_marker[i] >= other) {
  878.                     g_status.buff_marker[i] += len;
  879.                 }
  880.                 else if (g_status.buff_marker[i] > pos) {
  881.                     g_status.buff_marker[i] = pos;
  882.                 }
  883.             }
  884.         }
  885.     }
  886.  
  887.     /*
  888.      * If the window changed size, then it must have been edited in
  889.      *  some way.
  890.      */
  891.     window->file_info->modified = TRUE;
  892.     if (!g_status.unsaved) {
  893.         g_status.save_time = time(NULL);
  894.         g_status.unsaved = TRUE;
  895.     }
  896. }
  897.  
  898. /*
  899.  * Name:    get_ynaq
  900.  * Purpose: To input a response of yes, no, always or quit.
  901.  * Date:    October 1, 1989
  902.  * Passed:  window: access to the current window
  903.  * Returns: the user's answer (A_??? - see common.h)
  904.  */
  905. int get_ynaq(window)
  906. windows *window;
  907. {
  908.     char c;   /* user's response */
  909.  
  910.     /*
  911.      * leave the cursor marking the find / replace text
  912.      */
  913.     xygoto(window->ccol, window->cline);
  914.  
  915.     /*
  916.      * keep trying until the user enters something acceptable
  917.      */
  918.     for (;;) {
  919.         c = c_input();
  920.         if (hw_printable(c)) {
  921.             xygoto(g_status.prompt_col, g_status.prompt_line);
  922.             set_attr(g_display.flash);
  923.             c_output(c);
  924.         }
  925.         switch (toupper(c)) {
  926.         case 'Y':
  927.             return A_YES;
  928.         case 'N':
  929.             return A_NO;
  930.         case 'A':
  931.             return A_ALWAYS;
  932.         case 'Q':
  933.             return A_QUIT;
  934.         case 27:
  935.         case CONTROL('U'):
  936.             return A_ABORT;
  937.         default:
  938.             xygoto(window->ccol, window->cline);
  939.             break;
  940.         }
  941.     }
  942. }
  943.  
  944. /*
  945.  * Name:    get_yn
  946.  * Purpose: To input a response of yes or no.
  947.  * Date:    October 1, 1989
  948.  * Passed:  window: access to the current window (not used, but
  949.  *                   required to match other functions of type do_func)
  950.  * Returns: the user's answer (A_??? - see common.h)
  951.  */
  952. int get_yn(window)
  953. windows *window;
  954. {
  955.     char c;   /* the user's response */
  956.  
  957.     for (;;) {
  958.         xygoto(g_status.prompt_col, g_status.prompt_line);
  959.         c = c_input();
  960.         if (hw_printable(c)) {
  961.             set_attr(g_display.flash);
  962.             c_output(c);
  963.         }
  964.         switch (toupper(c)) {
  965.         case 'Y':
  966.             return A_YES;
  967.         case 'N':
  968.             return A_NO;
  969.         case 27:
  970.         case CONTROL('U'):
  971.             return A_ABORT;
  972.         default:
  973.             break;
  974.         }
  975.     }
  976. }
  977.  
  978. /*
  979.  * Name:    get_oa
  980.  * Purpose: To input a response of overwrite or append.
  981.  * Date:    October 1, 1989
  982.  * Passed:  window: access to the current window (not used, but
  983.  *                   required to match other functions of type do_func)
  984.  * Returns: the user's answer (A_??? - see common.h)
  985.  */
  986. int get_oa(window)
  987. windows *window;
  988. {
  989.     char c;   /* the user's response */
  990.  
  991.     for (;;) {
  992.         xygoto(g_status.prompt_col, g_status.prompt_line);
  993.         c = c_input();
  994.         if (hw_printable(c)) {
  995.             set_attr(g_display.flash);
  996.             c_output(c);
  997.         }
  998.         switch (toupper(c)) {
  999.         case 'O':
  1000.             return A_OVERWRITE;
  1001.         case 'A':
  1002.             return A_APPEND;
  1003.         case 27:
  1004.         case CONTROL('U'):
  1005.             return A_ABORT;
  1006.         default:
  1007.             break;
  1008.         }
  1009.     }
  1010. }
  1011.  
  1012. /*
  1013.  * Name:    get_attr
  1014.  * Purpose: To find what attribute should be displayed at the current
  1015.  *           location.
  1016.  * Date:    October 1, 1989
  1017.  * Passed:  window:   information allowing access to the current window
  1018.  *          text:     location to be considered
  1019.  * Returns: the attribute required
  1020.  */
  1021. char get_attr(window, text)
  1022. windows *window;
  1023. text_ptr text;
  1024. {
  1025.     char wanted;    /* wanted attribute */
  1026.     int marked;     /* was text possibly marked? */
  1027.  
  1028.     if (window == NULL) {
  1029.         /*
  1030.          * This is a status line
  1031.          */
  1032.         wanted = g_display.block;
  1033.     }
  1034.     else if (*text < 32 && *text != '\n' && *text != '\0') {
  1035.         /*
  1036.          * this is a control character, always "flashing"
  1037.          */
  1038.         wanted = g_display.flash;
  1039.     }
  1040.     else if (text >= g_status.match_start && text < g_status.match_end) {
  1041.         /*
  1042.          * this is used for marking matched text in find/replace
  1043.          */
  1044.         wanted = g_display.flash;
  1045.     }
  1046.     else if (!window->file_info->visible) {
  1047.         /*
  1048.          * no visible block, so return quickly
  1049.          */
  1050.         wanted = g_display.normal;
  1051.     }
  1052.     else if (text >= window->file_info->start_text &&
  1053.             text < window->file_info->end_text) {
  1054.         /*
  1055.          * the current location is in the main text
  1056.          */
  1057.         if (text >= window->file_info->marker[START_BLOCK] &&
  1058.                 text < window->file_info->marker[END_BLOCK]) {
  1059.             wanted = g_display.block;
  1060.         }
  1061.         else {
  1062.             wanted = g_display.normal;
  1063.         }
  1064.     }
  1065.     else {
  1066.         /*
  1067.          * the current location is in the current line buffer. Here,
  1068.          *  the text is marked if it is between the start and end
  1069.          *  markers within the current line, but it may also be marked
  1070.          *  if a block started before the current line, or ended
  1071.          *  after the current line.
  1072.          */
  1073.         if (g_status.buff_marker[START_BLOCK] == NULL) {
  1074.             marked = window->cursor > window->file_info->marker[START_BLOCK];
  1075.         }
  1076.         else {
  1077.             marked = text >= g_status.buff_marker[START_BLOCK];
  1078.         }
  1079.  
  1080.         if (marked) {
  1081.             if (g_status.buff_marker[END_BLOCK] == NULL) {
  1082.                 marked = window->cursor <
  1083.                         window->file_info->marker[END_BLOCK];
  1084.             }
  1085.             else {
  1086.                 marked = text < g_status.buff_marker[END_BLOCK];
  1087.             }
  1088.         }
  1089.  
  1090.         if (marked) {
  1091.             wanted = g_display.block;
  1092.         }
  1093.         else {
  1094.             wanted = g_display.normal;
  1095.         }
  1096.     }
  1097.     return wanted;
  1098. }
  1099.  
  1100. /*
  1101.  * this define is needed so we can display control characters
  1102.  *  sensibly
  1103.  */
  1104. #define fixup(c) ((c) < 32 ? (c)+'A'-1 : (c))
  1105.  
  1106. /*
  1107.  * Name:    update_line
  1108.  * Purpose: To make as few changes as possible to cause the current line
  1109.  *           to be what it should be.
  1110.  * Date:    October 1, 1989
  1111.  * Passed:  window:   information allowing access to the current window
  1112.  *          orig:     how the line SHOULD be
  1113.  *          line:     line number to be compared
  1114.  *          cursor:   the main text cursor location if line buffer is active
  1115.  * Returns: OK if line updated completely
  1116.  *          ERROR if update aborted by user typing a key
  1117.  * Notes:   This function checks both the text and the attributes, including
  1118.  *           the blank space beyond the right end of lines.
  1119.  *          The update is aborted if the user has typed a character.
  1120.  */
  1121. int update_line(window, orig, line, cursor)
  1122. windows *window;
  1123. text_ptr orig;
  1124. int line;
  1125. text_ptr cursor;
  1126. {
  1127.     int done = FALSE;   /* has line been completely compared? */
  1128.     text_ptr text;      /* current character of orig begin considered */
  1129.     int col;            /* update is current up to col */
  1130.     int new_col;        /* match is good up to new_col */
  1131.     int diff;           /* attribute was different */
  1132.     char wanted;        /* attribute wanted for current character */
  1133.     int check;          /* check for user input? */
  1134.  
  1135.     /*
  1136.      * return immediately if the user has typed a command.
  1137.      */
  1138.     if (c_avail()) {
  1139.         return ERROR;
  1140.     }
  1141.  
  1142.     /*
  1143.      * If this is the cursor line and it has been copied, then use the
  1144.      *  line buffer instead.
  1145.      */
  1146.     if (cursor == orig) {
  1147.         orig = g_status.line_buff;
  1148.         check = TRUE;
  1149.     }
  1150.     else {
  1151.         check = FALSE;
  1152.     }
  1153.  
  1154.     if (orig == NULL) {
  1155.         /*
  1156.          * we just want a blank line
  1157.          */
  1158.         new_col = 0;
  1159.         wanted = g_display.normal;
  1160.     }
  1161.     else {
  1162.         /*
  1163.          * Keep on patching differences until we reach the end of the
  1164.          *  text line
  1165.          */
  1166.         col = 0;
  1167.         for (;;) {
  1168.             diff = FALSE;
  1169.             text = orig + col;
  1170.             /*
  1171.              * see how far the lines match
  1172.              */
  1173.             for (new_col=col; ; new_col++, text++) {
  1174.                 if (*text == '\n') {
  1175.                     done = TRUE;
  1176.                     break;
  1177.                 }
  1178.                 if (*text == '\0') {
  1179.                     done = TRUE;
  1180.                     break;
  1181.                 }
  1182.                 if (new_col == g_display.ncols) {
  1183.                     done = TRUE;
  1184.                     break;
  1185.                 }
  1186.                 wanted = get_attr(window, text);
  1187.                 if (g_screen[line][new_col].attr != wanted) {
  1188.                     diff = TRUE;
  1189.                     break;
  1190.                 }
  1191.                 if (g_screen[line][new_col].c != fixup(*text)) {
  1192.                     break;
  1193.                 }
  1194.             }
  1195.             if (done) {
  1196.                 /*
  1197.                  * complete match up to end of text line
  1198.                  */
  1199.                 break;
  1200.             }
  1201.  
  1202.             /*
  1203.              * check for anything else to be before updating screen (but
  1204.              *  only if this is the cursor line)
  1205.              */
  1206.             if (check && c_avail()) {
  1207.                 return ERROR;
  1208.             }
  1209.  
  1210.             /*
  1211.              * use cursor addressing only if this means sending fewer
  1212.              *  characters to the terminal.
  1213.              * Often, the xygoto will be a no-op, since the cursor
  1214.              *  will already be in position.
  1215.              */
  1216.             if (new_col - col > g_display.ca_len || diff) {
  1217.                 col = new_col;
  1218.             }
  1219.             xygoto(col, line);
  1220.  
  1221.             /*
  1222.              * output the required character
  1223.              */
  1224.             text = orig + col++;
  1225.             wanted = get_attr(window, text);
  1226.             set_attr(wanted);
  1227.             c_output(fixup(*text));
  1228.         }
  1229.         /*
  1230.          * lines now match up to the end of the text line
  1231.          *
  1232.          * work out what attribute to use for the rest of the line
  1233.          */
  1234.         text = orig + new_col;
  1235.         wanted = get_attr(window, text);
  1236.     }
  1237.  
  1238.     /*
  1239.      * now make the rest of the line spaces with the right attribute
  1240.      */
  1241.     col = new_col;
  1242.     done = FALSE;
  1243.     for (;;) {
  1244.         diff = FALSE;
  1245.         for (new_col=col; ; new_col++) {
  1246.             if (new_col == g_display.ncols) {
  1247.                 done = TRUE;
  1248.                 break;
  1249.             }
  1250.             if (g_screen[line][new_col].attr != wanted) {
  1251.                 diff = TRUE;
  1252.                 break;
  1253.             }
  1254.             if (g_screen[line][new_col].c != ' ') {
  1255.                 break;
  1256.             }
  1257.         }
  1258.         if (done) {
  1259.             break;
  1260.         }
  1261.         if (check && c_avail()) {
  1262.             return ERROR;
  1263.         }
  1264.         if (new_col - col > g_display.ca_len || diff) {
  1265.             col = new_col;
  1266.         }
  1267.         xygoto(col, line);
  1268.  
  1269.         /*
  1270.          * the clear to end of line function is a quick way to get the
  1271.          *  normal attribute for the rest of the line. Unfortunately,
  1272.          *  it cannot be relied upon to set any other attribute!
  1273.          */
  1274.         if (wanted == g_display.normal) {
  1275.             if (eol_clear()) {
  1276.                 break;
  1277.             }
  1278.         }
  1279.         set_attr(wanted);
  1280.         c_output(' ');
  1281.         col++;
  1282.     }
  1283.     return OK;
  1284. }
  1285.  
  1286. /*
  1287.  * Name:    display_window
  1288.  * Purpose: To update one window to look the way it should, making as few
  1289.  *           changes as possible.
  1290.  * Date:    October 1, 1989
  1291.  * Passed:  window:   information allowing access to the current window
  1292.  *          last:     number of lines to update (so we can reserve lines
  1293.  *                     for things like answering prompts)
  1294.  *          cursor:   cursor line if line buffer active
  1295.  *          wn:       window number
  1296.  * Returns: OK if window updated completely
  1297.  *          ERROR if update aborted by user typing a key
  1298.  * Notes:   First the cursor line is updated, and then lines above and
  1299.  *           below the cursor are updated alternately.
  1300.  */
  1301. int display_window(window, last, cursor, wn)
  1302. windows *window;
  1303. int last;
  1304. text_ptr cursor;
  1305. int wn;
  1306. {
  1307.     text_ptr prev;      /* successive lines above the cursor */
  1308.     text_ptr next;      /* successive lines below the cursor */
  1309.     int line_above;     /* line number of lines above cursor */
  1310.     int line_below;     /* line number of lines below cursor */
  1311.     int count;          /* number of lines updated so far */
  1312.     int turn = FALSE;   /* turn to do above or below line? */
  1313.     int number;         /* number of lines visible in window */
  1314.     char status_line[MAX_COLS+1]; /* status line at top of window */
  1315.     char *p;            /* for setting up status line */
  1316.     char check;         /* used to check character before start of line */
  1317.     int len;            /* characters by which cursor should be adjusted */
  1318.  
  1319.     /*
  1320.      * work out bottom line (+1) to be displayed
  1321.      */
  1322.     if (window->bottom_line+last >= g_display.nlines) {
  1323.         last = g_display.nlines - last;
  1324.     }
  1325.     else {
  1326.         last = window->bottom_line + 1;
  1327.     }
  1328.  
  1329.     /*
  1330.      * work out how many lines need to be displayed
  1331.      */
  1332.     number = last - window->top_line;
  1333.  
  1334.     /*
  1335.      * display the required number of lines, starting from the
  1336.      *  cursor line
  1337.      */
  1338.     for (count=0; count < number; turn = !turn) {
  1339.         if (count == 0) {
  1340.             /*
  1341.              * as a result of editing in other windows into the same
  1342.              *  file, it is possible that the cursor position may be
  1343.              *  in the middle of a line.
  1344.              * If this is the case, then the cursor position must be
  1345.              *  adjusted so that the line can be displayed properly.
  1346.              */
  1347.             check = *(window->cursor-1);
  1348.             if (check != '\n' && check != '\0') {
  1349.                 len = prelinelen(window->cursor);
  1350.                 window->cursor -= len;
  1351.                 window->ccol += len;
  1352.                 if (window->ccol >= g_display.ncols) {
  1353.                     window->ccol = g_display.ncols - 1;
  1354.                 }
  1355.             }
  1356.  
  1357.             /*
  1358.              * if line is to be displayed, then update it
  1359.              */
  1360.             if (window->cline < last) {
  1361.                 if (update_line(window, window->cursor, window->cline,
  1362.                         cursor)) {
  1363.                     return ERROR;
  1364.                 }
  1365.             }
  1366.  
  1367.             /*
  1368.              * set up next and previous lines
  1369.              */
  1370.             next = find_next(window->cursor);
  1371.             prev = find_prev(window->cursor);
  1372.             line_above = window->cline - 1;
  1373.             line_below = window->cline + 1;
  1374.  
  1375.             /*
  1376.              * one more line has been displayed
  1377.              */
  1378.             ++count;
  1379.  
  1380.             if (wn == 0) {
  1381.                 /*
  1382.                  * move the cursor to its correct position, since often
  1383.                  *  other lines will not be affected.
  1384.                  */
  1385.                 xygoto(window->ccol, window->cline);
  1386.             }
  1387.         }
  1388.         else if (turn && line_below < last) {
  1389.             if (update_line(window, next, line_below, cursor)) {
  1390.                 return ERROR;
  1391.             }
  1392.             if (next) {
  1393.                 next = find_next(next);
  1394.             }
  1395.             ++count;
  1396.             ++line_below;
  1397.         }
  1398.         else if (!turn && line_above >= window->top_line) {
  1399.             if (update_line(window, prev, line_above, cursor)) {
  1400.                 return ERROR;
  1401.             }
  1402.             if (prev) {
  1403.                 prev = find_prev(prev);
  1404.             }
  1405.             ++count;
  1406.             --line_above;
  1407.         }
  1408.     }
  1409.  
  1410.     /*
  1411.      * display status line
  1412.      */
  1413.     sprintf(status_line, "== %s [%2d] ==", window->file_info->file_name, wn);
  1414.     count = strlen(status_line);
  1415.     p = status_line + count;
  1416.     while (count++ < g_display.ncols) {
  1417.         *p++ = '=';
  1418.     }
  1419.     *p = '\0';
  1420.  
  1421.     if (update_line(NULL, status_line, window->top_line-1, NULL)) {
  1422.         return ERROR;
  1423.     }
  1424.     return OK;
  1425. }
  1426.  
  1427. /*
  1428.  * Name:    display
  1429.  * Purpose: To update the display to look the way it should, making as few
  1430.  *           changes as possible.
  1431.  * Date:    October 1, 1989
  1432.  * Passed:  doit:     function to be called if a key is typed
  1433.  *          reserved: number of lines reserved for things like answering
  1434.  *                     prompts
  1435.  * Returns: the result returned by the function doit
  1436.  * Notes:   First the current window is updated, and then windows above and
  1437.  *           below the cursor are updated alternately.
  1438.  */
  1439. int display(doit, reserved)
  1440. do_func doit;
  1441. int reserved;
  1442. {
  1443.     windows *window;            /* current active window */
  1444.     int above_count;            /* window number above current */
  1445.     int below_count;            /* window number below current */
  1446.     windows *above;             /* window above current */
  1447.     windows *below;             /* window below current */
  1448.     text_ptr cursor;            /* cursor line in current window */
  1449.  
  1450.     /*
  1451.      * since some commands change the current window, display must check
  1452.      *  rather than relying on a passed parameter.
  1453.      */
  1454.     window = g_status.current_window;
  1455.  
  1456.     /*
  1457.      * If the line buffer is active, then other routines need to know
  1458.      *  which line should be taken from the line buffer instead.
  1459.      * This approach allows multiple windows into the same file to
  1460.      *  display the cursor line correctly.
  1461.      */
  1462.     if (g_status.copied) {
  1463.         cursor = window->cursor;
  1464.     }
  1465.     else {
  1466.         cursor = (text_ptr) -1; /* no line should start here! */
  1467.     }
  1468.  
  1469.     /*
  1470.      * display the current window
  1471.      */
  1472.     if (display_window(window, reserved, cursor, 0)) {
  1473.         return (*doit)(window);
  1474.     }
  1475.  
  1476.     /*
  1477.      * move the cursor to its correct position, since usually other
  1478.      *  windows will not be affected.
  1479.      */
  1480.     xygoto(window->ccol, window->cline);
  1481.  
  1482.     /*
  1483.      * now update all the other windows
  1484.      */
  1485.     above = below = window;
  1486.     above_count = below_count = 0;
  1487.     while (above->prev || below->next) {
  1488.         if (above->prev) {
  1489.             above = above->prev;
  1490.             --above_count;
  1491.             if (display_window(above, reserved, cursor, above_count)) {
  1492.                 return (*doit)(window);
  1493.             }
  1494.         }
  1495.         if (below->next) {
  1496.             below = below->next;
  1497.             ++below_count;
  1498.             if (display_window(below, reserved, cursor, below_count)) {
  1499.                 return (*doit)(window);
  1500.             }
  1501.         }
  1502.     }
  1503.  
  1504.     /*
  1505.      * all done, so position the cursor and wait for the user to enter
  1506.      *  something
  1507.      */
  1508.     xygoto(window->ccol, window->cline);
  1509.     return (*doit)(window);
  1510. }
  1511.  
  1512. /*
  1513.  * Name:    setup_window
  1514.  * Purpose: To set the page length and the center line of a window, based
  1515.  *           on the top and bottom lines.
  1516.  * Date:    October 10, 1989
  1517.  * Passed:  window: window to be set up
  1518.  */
  1519. void setup_window(window)
  1520. windows *window;
  1521. {
  1522.     window->place_line = (window->bottom_line + window->top_line) / 2;
  1523.     window->page = window->bottom_line - window->top_line -
  1524.             g_status.overlap + 1;
  1525.     if (window->page < 1) {
  1526.         window->page = 1;
  1527.     }
  1528. }
  1529.  
  1530. /*
  1531.  * Name:    first_non_blank
  1532.  * Purpose: To find the column in which the first non-blank character in
  1533.  *           the string occurs.
  1534.  * Date:    October 1, 1989
  1535.  * Passed:  s:  the string to search
  1536.  * Returns: the first non-blank column
  1537.  */
  1538. int first_non_blank(s)
  1539. char *s;
  1540. {
  1541.     int count = 0;
  1542.  
  1543.     while (*s && *s++ == ' ') {
  1544.         ++count;
  1545.     }
  1546.     return count;
  1547. }
  1548.  
  1549. /*
  1550.  * Name:    page_up
  1551.  * Purpose: To move the cursor one page up the window (probably more
  1552.  *           intuitive to think of the text being moved down)
  1553.  * Date:    October 1, 1989
  1554.  * Passed:  window:   information allowing access to the current window
  1555.  * Notes:   The cursor line is moved back the required number of lines
  1556.  *           towards the start of the file.
  1557.  *          If the start of the file is reached, then the movement stops.
  1558.  *           In this case, the cursor is placed at the top of the window.
  1559.  */
  1560. void page_up(window)
  1561. windows *window;
  1562. {
  1563.     int i;        /* count of lines scanned */
  1564.     text_ptr p;   /* previous lines */
  1565.  
  1566.     un_copy_line(window);
  1567.     for (i=0; i < window->page; i++) {
  1568.         if ((p = find_prev(window->cursor)) != NULL) {
  1569.             window->cursor = p;
  1570.         }
  1571.         else {
  1572.             window->cline = window->top_line;
  1573.             break;
  1574.         }
  1575.     }
  1576. }
  1577.  
  1578. /*
  1579.  * Name:    page_down
  1580.  * Purpose: To move the cursor one page down the window (probably more
  1581.  *           intuitive to think of the text being moved up)
  1582.  * Date:    October 1, 1989
  1583.  * Passed:  window:   information allowing access to the current window
  1584.  * Notes:   The cursor line is moved forwards the required number of lines
  1585.  *           towards the end of the file.
  1586.  *          If the end of the file is reached, then the movement stops.
  1587.  *           In this case, the cursor is placed at the bottom of the window.
  1588.  */
  1589. void page_down(window)
  1590. windows *window;
  1591. {
  1592.     int i;          /* count of lines scanned so far */
  1593.     text_ptr p;     /* lines below cursor */
  1594.  
  1595.     un_copy_line(window);
  1596.     for (i=0; i < window->page; i++) {
  1597.         if ((p = find_next(window->cursor)) != NULL) {
  1598.             window->cursor = p;
  1599.         }
  1600.         else {
  1601.             window->cline = window->bottom_line;
  1602.             break;
  1603.         }
  1604.     }
  1605. }
  1606.  
  1607. /*
  1608.  * Name:    scroll_down
  1609.  * Purpose: To make the necessary changes after the user has given the
  1610.  *           command to scroll down the screen.
  1611.  * Date:    October 1, 1989
  1612.  * Passed:  window: information allowing access to the current window
  1613.  * Notes:   Normally, we can just delete the top line on the window, and
  1614.  *           then move the cursor up one line (so the cursor remains at
  1615.  *           the same position in the file).
  1616.  *          However, if the cursor was already on the top line of the
  1617.  *           window, then the cursor must be moved down a line first.
  1618.  */
  1619. void scroll_down(window)
  1620. windows *window;
  1621. {
  1622.     text_ptr next;
  1623.  
  1624.     if (window->cline == window->top_line) {
  1625.         /*
  1626.          * Since the cursor must be moved, it is necessary to flush the
  1627.          *  current line buffer back into the main text.
  1628.          * If the line was not already copied, then this function will
  1629.          *  have no effect.
  1630.          */
  1631.         un_copy_line(window);
  1632.  
  1633.         if ((next = find_next(window->cursor)) != NULL) {
  1634.             window->cursor = next;
  1635.         }
  1636.         else {
  1637.             return;
  1638.         }
  1639.         ++window->cline;
  1640.     }
  1641.  
  1642.     /*
  1643.      * Note that in order to scroll the window down the file, we must
  1644.      *  scroll the text UP the screen!
  1645.      */
  1646.     window_scroll_up(window->top_line, window->bottom_line);
  1647.     --window->cline;
  1648. }
  1649.  
  1650. /*
  1651.  * Name:    scroll_up
  1652.  * Purpose: To make the necessary changes after the user has given the
  1653.  *           command to scroll up the screen.
  1654.  * Date:    October 1, 1989
  1655.  * Passed:  window: information allowing access to the current window
  1656.  * Notes:   Normally, we can just insert one line at the top of the window,
  1657.  *           and then move the cursor down one line (so the cursor remains at
  1658.  *           the same position in the file).
  1659.  *          However, if the cursor was already on the bottom line of the
  1660.  *           window, then the cursor must be moved up a line first.
  1661.  */
  1662. void scroll_up(window)
  1663. windows *window;
  1664. {
  1665.     text_ptr prev;
  1666.  
  1667.     if (window->cline == window->bottom_line) {
  1668.         un_copy_line(window);
  1669.         if ((prev = find_prev(window->cursor)) != NULL) {
  1670.             window->cursor = prev;
  1671.         }
  1672.         else {
  1673.             return;
  1674.         }
  1675.         --window->cline;
  1676.     }
  1677.     window_scroll_down(window->top_line, window->bottom_line);
  1678.     ++window->cline;
  1679. }
  1680.  
  1681. /*
  1682.  * Name:    save_file
  1683.  * Purpose: To save the current file to disk.
  1684.  * Date:    October 1, 1989
  1685.  * Passed:  window:   information allowing access to the current window
  1686.  *          kind:     whether we are saving the recovery file or not
  1687.  * Notes:   The file is first written under a temporary name, and only
  1688.  *           once this is successful is the original file removed.
  1689.  *          File names may not contain "/", "\" or ":" characters, or
  1690.  *           the editor will get confused when creating the temporary
  1691.  *           name.
  1692.  *          If anything goes wrong, then the modified flag is set.
  1693.  *          If the file is saved successfully, then modified flag is
  1694.  *           cleared.
  1695.  */
  1696. void save_file(window, kind)
  1697. windows *window;
  1698. int kind;
  1699. {
  1700.     char name[MAX_COLS]; /* name of file to be saved */
  1701.     char temp[MAX_COLS]; /* temporary file name */
  1702.     int *pmodified;      /* modified flag location */
  1703.     int new_file;        /* are we saving a new file? */
  1704.     int *pnew_file;      /* location of above */
  1705.  
  1706.     /*
  1707.      * make sure we are writing the latest version of the current line
  1708.      */
  1709.     un_copy_line(window);
  1710.  
  1711.     /*
  1712.      * set up file name and location of various flags depending on
  1713.      *  whether we are writing a normal file or a recovery file
  1714.      */
  1715.     if (kind == SAVE_NORMAL) {
  1716.         strcpy(name, window->file_info->file_name);
  1717.         pmodified = &(window->file_info->modified);
  1718.         pnew_file = &(window->file_info->new_file);
  1719.     }
  1720.     else {
  1721.         if (g_status.recovery[0] == '\0') {
  1722.             hw_copy_path(window->file_info->file_name, RECOVERY,
  1723.                     g_status.recovery);
  1724.         }
  1725.         strcpy(name, g_status.recovery);
  1726.         pmodified = &(g_status.unsaved);
  1727.         new_file = hw_fattrib(g_status.recovery) == ERROR;
  1728.         pnew_file = &new_file;
  1729.     }
  1730.  
  1731.     /*
  1732.      * see if there was a file name - if not, then make the user
  1733.      *  supply one.
  1734.      */
  1735.     if (strlen(name) == 0) {
  1736.         save_as_file(window);
  1737.         return;
  1738.     }
  1739.  
  1740.     /*
  1741.      * It is not safe to simply overwrite the old file, since a system
  1742.      *  crash could cause both the old and edited versions to be lost!
  1743.      * Hence we write to a temporary file first.
  1744.      */
  1745.     strcpy(temp, name);
  1746.     if (!(*pnew_file)) {
  1747.         hw_copy_path(name, "DTXXXXXX", temp);
  1748.         mktemp(temp);
  1749.     }
  1750.  
  1751.     /*
  1752.      * save the file
  1753.      */
  1754.     error(TEMP, "Saving '%s'", name);
  1755.     if (hw_save(temp, window->file_info->start_text,
  1756.             window->file_info->end_text-1) == ERROR) {
  1757.         if (kind == SAVE_NORMAL) {
  1758.             error(WARNING, "cannot write to '%s'", temp);
  1759.         }
  1760.         else {
  1761.             error(WARNING, "cannot write recovery file");
  1762.             /*
  1763.              * if we cannot write to the recovery file, then do not
  1764.              *  try to write again for at least the normal interval
  1765.              */
  1766.             g_status.save_time += g_status.save_interval;
  1767.         }
  1768.         return;
  1769.     }
  1770.     *pmodified = FALSE;
  1771.  
  1772.     /*
  1773.      * If everything went OK, then rename files and remove the original
  1774.      */
  1775.     if (!(*pmodified)) {
  1776.         if (*pnew_file) {
  1777.             /*
  1778.              * if the file was new, then it is no longer new
  1779.              */
  1780.             *pnew_file = FALSE;
  1781.  
  1782.             if (kind == SAVE_NORMAL && g_status.recovery[0]) {
  1783.                 /*
  1784.                  * if the main file has been saved, then the recovery
  1785.                  *  file is redundant
  1786.                  */
  1787.                 hw_unlink(g_status.recovery);
  1788.                 g_status.recovery[0] = '\0';
  1789.             }
  1790.             g_status.unsaved = FALSE;
  1791.             return;
  1792.         }
  1793.  
  1794.         /*
  1795.          * if the file already existed, then now is the time to remove
  1796.          *  the original file and rename the temporary one.
  1797.          */
  1798.         if (hw_unlink(name) == ERROR) {
  1799.             error(WARNING, "error deleting '%s'", name);
  1800.             *pmodified = TRUE;
  1801.         }
  1802.         else {
  1803.             if (hw_rename(temp, name) == ERROR) {
  1804.                 error(WARNING, "error renaming '%s' to '%s'",
  1805.                         temp, name);
  1806.                 *pmodified = TRUE;
  1807.             }
  1808.             else { /* complete success - now change access modes */
  1809.                 hw_set_fattrib(name, window->file_info->file_attrib);
  1810.                 if (kind == SAVE_NORMAL && g_status.recovery[0]) {
  1811.                     hw_unlink(g_status.recovery);
  1812.                     g_status.recovery[0] = '\0';
  1813.                 }
  1814.                 g_status.unsaved = FALSE;
  1815.             }
  1816.         }
  1817.     }
  1818. }
  1819.  
  1820. /*
  1821.  * Name:    save_as_file
  1822.  * Purpose: To save the current file to disk, but under a new name.
  1823.  * Date:    October 1, 1989
  1824.  * Passed:  window:   information allowing access to the current window
  1825.  */
  1826. void save_as_file(window)
  1827. windows *window;
  1828. {
  1829.     char name[MAX_COLS];   /* new name for file */
  1830.  
  1831.     /*
  1832.      * make sure we are writing the latest version of the current line
  1833.      */
  1834.     un_copy_line(window);
  1835.  
  1836.     /*
  1837.      * read in name, no default
  1838.      */
  1839.     name[0] = '\0';
  1840.     if (get_name("New file name: ", 1, name) != OK) {
  1841.         return;
  1842.     }
  1843.  
  1844.     /*
  1845.      * make sure it is OK to overwrite any existing file
  1846.      */
  1847.     if (hw_fattrib(name) != ERROR) { /* file exists */
  1848.         set_prompt("Overwrite existing file? (y/n): ", 1);
  1849.         if (display(get_yn, 1) != A_YES) {
  1850.             return;
  1851.         }
  1852.         if (hw_unlink(name) == ERROR) {
  1853.             return;
  1854.         }
  1855.     }
  1856.  
  1857.     /*
  1858.      * record the new file name
  1859.      */
  1860.     strcpy(window->file_info->file_name, name);
  1861.  
  1862.     /*
  1863.      * save the file, maintaining attributes
  1864.      */
  1865.     error(TEMP, "Saving '%s'", name);
  1866.     if (hw_save(name, window->file_info->start_text,
  1867.             window->file_info->end_text-1) == ERROR) {
  1868.         error(WARNING, "cannot write to '%s'", name);
  1869.         return;
  1870.     }
  1871.     hw_set_fattrib(name, window->file_info->file_attrib);
  1872.  
  1873.     /*
  1874.      * record that file is saved and not yet modified again
  1875.      */
  1876.     window->file_info->modified = FALSE;
  1877.     if (g_status.recovery[0]) {
  1878.         hw_unlink(g_status.recovery);
  1879.         g_status.recovery[0] = '\0';
  1880.     }
  1881.     g_status.unsaved = FALSE;
  1882. }
  1883.  
  1884.